#include "EventFileDriver.h"

#include "EventNode.h"
#include "EventString.h"

#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _(String) gettext(String)

static char magic[10] = { 'E', 'F', 'D', 0, 0, 0, 0, 0, 0, 0 }, T = 'T',
	    E = 'E';

struct _EventFileDriverPriv
{
	/* 文件的路径 */
	EventString *path;

	/* 数据条目数 */
	eint32 items_num;

	/* 若已成功加载数据，为 true */
	bool is_loaded;

	/**
         * 解析一个条目的方法
         * @param self : 对象本身
         * @param data : 解析的数据
         * @param length : 解析的数据大小
         *
         * @return : 0 成功，1 失败
         */
	int (*parser)(EventFileDriver *self, const epointer data, int length);

	/**
         * 反解析一个条目的方法
         * @param self : 数据表对象本身，需要其可迭代
         *
         * @return : 返回迭代的一个数据包
         */
	EventFileDriverPack *(*writer)(EventFileDriver *self);

	/**
         * 反解析前提示对象准备数据的方法，如果没有就不传递该函数。
         * @param self : 数据表对象本身。
         *
         * @return 返回值：EVENT_FILE_DRIVER_SUCCESS,
         * EVENT_FILE_DRIVER_PREPARE_DATA_ERROR
         */
	int (*prepare_data)(EventFileDriver *self);

	/**
         * 若文件不存在，是否直接返回错误，或者创建新文件
         */
	bool will_throw;
};

struct _EventFileDriverPack
{
	epointer data;
	euint32	 length;
};

void
event_file_driver_finalize(EventFileDriver *self)
{
	event_object_unref(self->priv->path);
	free(self->priv);
}

void
event_file_driver_init(EventFileDriver *object)
{
	EventFileDriverPriv *priv;

	event_object_init(EVENT_OBJECT(object));
	event_object_set_finalize(object, FINALIZE(event_file_driver_finalize));

	object->priv = MALLOC_NEW(EventFileDriverPriv);
	priv	     = object->priv;

	priv->items_num	   = 0;
	priv->parser	   = NULL;
	priv->writer	   = NULL;
	priv->prepare_data = NULL;
	priv->path	   = NULL;
	priv->is_loaded	   = false;
	priv->will_throw   = true;
}

void
event_file_driver_set_file_not_exist_throw(EventFileDriver *self,
					   bool		    will_throw)
{
	return_if_fail(self != NULL,
		       event_file_driver_set_file_not_exist_throw);
	self->priv->will_throw = will_throw;
}

void
event_file_driver_set_parser(EventFileDriver *self,
			     int (*parser)(EventFileDriver *, const epointer,
					   int))
{
	return_if_fail(self != NULL, event_file_driver_set_parser);
	return_if_fail(parser != NULL, event_file_driver_set_parser);
	self->priv->parser = parser;
}

void
event_file_driver_set_writer(EventFileDriver	 *self,
			     EventFileDriverPack *(*writer)(EventFileDriver *),
			     int (*prepare_data)(EventFileDriver *))
{
	return_if_fail(self != NULL, event_file_driver_set_writer);
	return_if_fail(writer != NULL, event_file_driver_set_parser);
	self->priv->writer	 = writer;
	self->priv->prepare_data = prepare_data;
}

void
event_file_driver_set_file(EventFileDriver *self, const char *path)
{
	return_if_fail(self != NULL, event_file_driver_set_file);
	return_if_fail(path != NULL, event_file_driver_set_file);
	self->priv->path = event_string_new(path);
}

static int
compare_by_byte(const char *data1, const char *data2, int times)
{
	for (int i = 0; i < times; ++i)
		if (data1[i] != data2[i])
			return 1;
	return 0;
}

/**
 * 检测文件是否是 EventServer 可读取的数据表
 */
static int
check_magic(FILE *fp)
{
	char buf[5] = { 0 }, magic[4] = { 'E', 'F', 'D', '\0' };

	rewind(fp);
	fread(buf, 4, 1, fp);
	return compare_by_byte(buf, magic, 4);
}

/**
 * 检测数据表的完整性
 */
static int
check_integrity(FILE *fp, EventFileDriverPriv *priv)
{
	int  items_number, item_size, verity_count = 0;
	char flag;

	if (fseek(fp, ITEMS_NUM_START, SEEK_SET))
		return EVENT_FILE_DRIVER_FILE_IMCOMPLETE_ERROR;
	fread((void *)&items_number, sizeof(int), 1, fp);
	if (items_number < 0 ||
	    items_number > EVENT_FILE_DRIVER_MAX_ITEMS_NUMBER)
		return EVENT_FILE_DRIVER_ITEMS_NUMBER_ERROR;
	priv->items_num = items_number;
re_detect:
	fread((void *)&flag, 1, 1, fp);
	if (flag != 'T' && flag != 'E')
		return EVENT_FILE_DRIVER_FILE_IMCOMPLETE_ERROR;
	if (flag == 'E') {
		if (items_number == verity_count)
			return 0;
		else
			return EVENT_FILE_DRIVER_FILE_IMCOMPLETE_ERROR;
	}
	fread((void *)&item_size, sizeof(int), 1, fp);
	if (item_size <= 0)
		return EVENT_FILE_DRIVER_ITEM_SIZE_ERROR;
	if (fseek(fp, item_size, SEEK_CUR))
		return EVENT_FILE_DRIVER_FILE_IMCOMPLETE_ERROR;
	verity_count += 1;
	goto re_detect;
}

static int
real_loader(EventFileDriver *self, FILE *fp)
{
	EventFileDriverPriv *priv = self->priv;
	char		    *raw, *p, *buffer;
	long		     file_length;
	int		     item_size, status;

	fseek(fp, 0, SEEK_END);
	file_length = ftell(fp);
	rewind(fp);

	raw = (char *)malloc(file_length);
	fread(raw, file_length, 1, fp);
	fclose(fp);
	p = raw + ITEM_START + 1;

	for (int i = 0; i < priv->items_num; ++i) {
		item_size = *((int *)p);
		p	 += sizeof(int);
		buffer	  = (char *)malloc(item_size);
		memcpy(buffer, p, item_size);
		/* 调用数据解析方法 */
		status = priv->parser(self, buffer, item_size);
		free(buffer);
		if (status) {
			free(raw);
			return EVENT_FILE_DRIVER_PARSER_DATA_ERROR;
		}
		p += (item_size + 1);
	}
	free(raw);
	priv->is_loaded = true;
	return EVENT_FILE_DRIVER_SUCCESS;
}

int
event_file_driver_loader(EventFileDriver *self)
{
	FILE *fp;
	int   status;

	return_val_if_fail(self != NULL, event_file_driver_loader,
			   EVENT_FILE_DRIVER_NULL_OBJECT_ERROR);

	if (self->priv->is_loaded)
		return EVENT_FILE_DRIVER_TABLE_LOADED_ERROR;

	if (!self->priv->path)
		return EVENT_FILE_DRIVER_PATH_NOT_SET_ERROR;

	if (!self->priv->parser)
		return EVENT_FILE_DRIVER_NOT_HAVE_PARSER;

	/* 不存在则创建新文件 */
	fp = fopen(ES_GET(self->priv->path), "r");
	if (!fp) {
		if (self->priv->will_throw)
			return EVENT_FILE_DRIVER_FILE_OPEN_ERROR;
		else
			return event_file_driver_create(self);
	}

	if (check_magic(fp)) {
		fclose(fp);
		return EVENT_FILE_DRIVER_MAGIC_CHECK_ERROR;
	}

	status = check_integrity(fp, self->priv);
	if (status) {
		fclose(fp);
		return status;
	}

	return real_loader(self, fp);
}

static void
pack_delete(epointer _pack)
{
	EventFileDriverPack *pack = EVENT_FILE_DRIVER_PACK(_pack);
	return_if_fail(pack != NULL, pack_delete);
	if (pack->data)
		free(pack->data);
	free(pack);
}

static int
real_dump(EventNode *pack_head, EventFileDriverPriv *priv)
{
	FILE		    *fp;
	EventNode	    *node = pack_head;
	EventFileDriverPack *pack;

	fp = fopen(ES_GET(priv->path), "w");
	if (!fp) {
		event_object_unref(pack_head);
		return EVENT_FILE_DRIVER_FILE_OPEN_ERROR;
	}

	fwrite(magic, 10, 1, fp);
	fwrite(&priv->items_num, sizeof(eint32), 1, fp);

	while (node != NULL && node->data != NULL) {
		pack = EVENT_FILE_DRIVER_PACK(node->data);
		fwrite(&T, 1, 1, fp);
		fwrite(&pack->length, sizeof(int), 1, fp);
		fwrite(pack->data, pack->length, 1, fp);
		node = node->next;
	}
	fwrite(&E, 1, 1, fp);
	fclose(fp);
	event_object_unref(pack_head);
	return EVENT_FILE_DRIVER_SUCCESS;
}

int
event_file_driver_dump(EventFileDriver *self)
{
	EventNode	    *pack_head, *pack_current;
	EventFileDriverPriv *priv = self->priv;
	EventFileDriverPack *pack;
	int		     status;

	return_val_if_fail(self != NULL, event_file_driver_dump,
			   EVENT_FILE_DRIVER_NULL_OBJECT_ERROR);

	if (!priv->is_loaded)
		return EVENT_FILE_DRIVER_TABLE_NOT_LOAD_ERROR;

	if (priv->prepare_data) {
		status = priv->prepare_data(self);
		if (status)
			return status;
	}

	pack_current = pack_head = event_node_new();

	for (int i = 0; i < priv->items_num; ++i) {
		pack = priv->writer(self);
		if (!pack) {
			event_object_unref(pack_head);
			return EVENT_FILE_DRIVER_WRITER_PACK_ERROR;
		}
		event_node_set_data(pack_current, pack, pack_delete);
		if (i < priv->items_num - 1) {
			event_node_set_next(pack_current, event_node_new());
			pack_current = pack_current->next;
		}
	}

	return real_dump(pack_head, priv);
}

int
event_file_driver_create(EventFileDriver *self)
{
	FILE *fp;
	int   item_num = 0;

	return_val_if_fail(self != NULL, event_file_driver_create,
			   EVENT_FILE_DRIVER_NULL_OBJECT_ERROR);
	if (!self->priv->path)
		return EVENT_FILE_DRIVER_PATH_NOT_SET_ERROR;
	fp = fopen(ES_GET(self->priv->path), "w");
	return_val_if_fail(fp != NULL, event_file_driver_create,
			   EVENT_FILE_DRIVER_FILE_OPEN_ERROR);

	fwrite(magic, 10, 1, fp);
	fwrite(&item_num, sizeof(int), 1, fp);
	fwrite(&E, 1, 1, fp);
	fclose(fp);
	self->priv->is_loaded = true;
	return EVENT_FILE_DRIVER_SUCCESS;
}

void
event_file_driver_add_count_once(EventFileDriver *self)
{
	return_if_fail(self != NULL, event_file_driver_add_count_once);
	self->priv->items_num += 1;
}

void
event_file_driver_reduce_count_once(EventFileDriver *self)
{
	return_if_fail(self != NULL, event_file_driver_reduce_count_once);
	if (self->priv->items_num > 0)
		self->priv->items_num -= 1;
}

EventFileDriverPack *
event_file_driver_pack_new(epointer data, euint32 length)
{
	EventFileDriverPack *pack = MALLOC_NEW(EventFileDriverPack);
	//
	pack->data   = data;
	pack->length = length;
	return pack;
}

EventFileDriverPack *
event_file_driver_pack_new_from_raw(EventRaw *raw)
{
	EventFileDriverPack *pack;

	return_val_if_fail(raw != NULL, event_file_driver_pack_new_from_raw,
			   NULL);
	pack	     = MALLOC_NEW(EventFileDriverPack);
	pack->length = event_raw_get_size(raw);
	pack->data   = event_raw_get_data(raw);
	free(raw);

	return pack;
}

void
event_file_driver_flush_items_num(EventFileDriver *self, eint32 items_num)
{
	return_if_fail(self != NULL, event_file_driver_flush_items_num);
	self->priv->items_num = items_num;
}
